﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace DDD.Infrastructure.Extend
{
   public  static class QueryableExtensions
    {

        /// <summary>
        /// 扩展Between 操作符
        /// </summary>
        /// <typeparam name="TSource">动态实体</typeparam>
        /// <typeparam name="TKey">键</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="keySelector">键选择器</param>
        /// <param name="low">低</param>
        /// <param name="high">高</param>
        /// <returns></returns>
        public static IQueryable<TSource> Between<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector,
            TKey low, TKey high) where TKey : IComparable<TKey>
        {

            Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(source.GenerateBetween(keySelector, low, high), keySelector.Parameters);
            return source.Where(lambda);
        }


        /// <summary>
        /// 扩展NotBetween操作符
        /// </summary>
        /// <typeparam name="TSource">动态实体</typeparam>
        /// <typeparam name="TKey">键</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="keySelector">键选择器</param>
        /// <param name="low">低</param>
        /// <param name="high">高</param>
        /// <returns></returns>
        public static IQueryable<TSource> NotBetween<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector,
            TKey low, TKey high) where TKey : IComparable<TKey>
        {
            //取反
            var not = Expression.Not(source.GenerateBetween(keySelector, low, high));
            Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(not, keySelector.Parameters);
            return source.Where(lambda);
        }
        /// <summary>
        /// 生成Between操作符
        /// </summary>
        /// <typeparam name="TSource">动态实体</typeparam>
        /// <typeparam name="TKey">键</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="keySelector">键选择器</param>
        /// <param name="low">低</param>
        /// <param name="high">高</param>
        /// <returns></returns>

        private static Expression GenerateBetween<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector,
            TKey low, TKey high) where TKey : IComparable<TKey>
        {
            source.NotNull(nameof(source));
            keySelector.NotNull(nameof(keySelector));
            low.NotNull(nameof(low));
            high.NotNull(nameof(high));
            Expression key = Expression.Invoke(keySelector, keySelector.Parameters);
            Expression lowerBound = Expression.GreaterThanOrEqual(key, Expression.Constant(low)); //18
            Expression upperBound = Expression.LessThanOrEqual(key, Expression.Constant(high)); //21
            Expression and = Expression.AndAlso(lowerBound, upperBound);
            return and;
        }


        /// <summary>
        /// 开始日期、截止日期。
        /// </summary>
        /// <typeparam name="T">动态对象</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="getter"></param>
        /// <param name="from">来自</param>
        /// <param name="to">到</param>
        /// <returns></returns>
        public static IQueryable<T> DateRange<T>(this IQueryable<T> source, Expression<Func<T, DateTime>> getter, DateTime from, DateTime to)
        {
            source.NotNull(nameof(source));
            getter.NotNull(nameof(getter));

            Expression body = getter.Body;
            var greaterExp = Expression.GreaterThanOrEqual(body, Expression.Constant(from));
            var lessExp = Expression.LessThanOrEqual(body, Expression.Constant(to));
            var and = Expression.And(greaterExp, lessExp);
            var lambda = Expression.Lambda<Func<T, bool>>(and, getter.Parameters);
            return source.Where(lambda);
        }


   
    }
}
